home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
tran2d.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
41KB
|
1,582 lines
/*
* $Id: tran2d.c,v 0.91 1994/02/20 00:53:14 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* General 2D raster transformations: X'= M (X-S) where S is the
* translational vector.
*
* We need this stuff because currently rotation is implemented this
* way. Once the three shear is implemented, general transformation
* will be of no use in bit program.
*
* Scaling is already implemented using different method.
*
*/
#if !defined(lint) && defined(F_ID)
char *id_tran = "$Id: tran2d.c,v 0.91 1994/02/20 00:53:14 zhao Pre-Release $";
#endif
#include "bit.h"
#include "dmalloc.h"
/******************** Local variables ***************************/
static int subpix; /* if to do anti-aliasing */
static int row, col, nrow, ncol;/* old the new size */
/*** input size and output size ****/
static void
set_tran_size(int oldrow, int oldcol, int newrow, int newcol)
{
row = oldrow;
col = oldcol;
nrow = newrow;
ncol = newcol;
}
/*** if subpixel sampling is desired ***/
static void
set_tran_subpix(int subp)
{
subpix = subp;
}
/****** pre-compute the x(column) transformations ********/
#define PRE_tran(prex, prey, func) \
do { \
int c_; \
\
prex = malloc(sizeof(float) * (ncol + 1)); \
prey = malloc(sizeof(float) * (ncol + 1)); \
if (!prex || !prey) { \
Bark(func, "malloc failed"); \
return -1; \
} \
for (c_ = 0; c_ <= ncol; c_++) { \
prex[c_] = m[0][0] * (c_ - shift[0]); \
prey[c_] = m[1][0] * (c_ - shift[0]); \
} \
} while(ZERO)
#define POST_tran(prex, prey) \
free(prex); \
free(prey)
/*** check if transformed coodinates are outside image **********/
#define OutsideSRC(c,ir,ic) ((ir = (prey[c] + tmpy)) < 0 || ir >= row ||\
((ic = (prex[c] + tmpx)) < 0 || ic >= col))
/***************************************************************
* transform a colorindex based raster image
****************************************************************/
static int
tran2d_ci(ci_t **in, ci_t **out,
float m[][2], float shift[], ci_t fill)
{
register ci_t *ras = out[0];
register float *prex, *prey, tmpy, tmpx;
register int c, ic, ir, r;
long rlines;
PRE_tran(prex, prey, "tran2d_ci");
rlines = progress_report("Tran2D_ci ...", nrow);
for (r = 0; r < nrow; r++)
{
REPORT(r, rlines);
tmpx = m[0][1] * (r - shift[1]) + 0.1;
tmpy = m[1][1] * (r - shift[1]) + 0.1;
for (c = 0; c < ncol; c++, ras++)
*ras = OutsideSRC(c, ir, ic) ? fill : in[ir][ic];
}
POST_tran(prex, prey);
remove_progress_report();
return 0;
}
/*************************************************************
* Transform a packed pixel based image
*************************************************************/
static int
tran2d_cpack(rgba_t **in, rgba_t **out, float m[][2],
float shift[], rgba_t fill)
{
register rgba_t *ras = out[0];
register float *prex, *prey, tmpy, tmpx;
register int c, ic, ir, r;
long rlines;
PRE_tran(prex, prey, "tran2d_cpack");
rlines = progress_report("Tran2D_cpack ...", nrow);
for (r = 0; r < nrow; r++)
{
REPORT(r, rlines);
tmpx = m[0][1] * (r - shift[1]) + 0.5;
tmpy = m[1][1] * (r - shift[1]) + 0.5;
for (c = 0; c < ncol; c++, ras++)
*ras = OutsideSRC(c, ir, ic) ? fill : in[ir][ic];
}
POST_tran(prex, prey);
remove_progress_report();
return 0;
}
/*********************************************************************
* Interpolate at (x,y) given in[1-3]. minimum x and y should not be
* smaller than -1 and maximum x and y should not be greater than col
* and row.
********************************************************************/
static void
frac(pc_t *po1, pc_t **in1, pc_t *po2, pc_t **in2,
pc_t *po3, pc_t **in3, float x, float y, int fill[])
{
float pdx[4], pdy[4], pdxy[4], ff[4], dx, dy, dxy;
float f00[4], f01[4], f10[4], f11[4];
int i, ix1, ix2, iy1, iy2;
ix1 = (x < 0.0) ? -1 : x;
iy1 = (y < 0.0) ? -1 : y;
ix2 = ix1 + 1;
iy2 = iy1 + 1;
i = (iy1 >= 0 && ix1 >= 0);
f00[1] = i ? in1[iy1][ix1] : fill[0];
f00[2] = i ? in2[iy1][ix1] : fill[1];
f00[3] = i ? in3[iy1][ix1] : fill[2];
i = (iy1 >= 0 && ix2 < col);
f01[1] = i ? in1[iy1][ix2] : fill[0];
f01[2] = i ? in2[iy1][ix2] : fill[1];
f01[3] = i ? in3[iy1][ix2] : fill[2];
i = (iy2 < row && ix1 >= 0);
f10[1] = i ? in1[iy2][ix1] : fill[0];
f10[2] = i ? in2[iy2][ix1] : fill[1];
f10[3] = i ? in3[iy2][ix1] : fill[2];
i = (iy2 < row && ix2 < col);
f11[1] = i ? in1[iy2][ix2] : fill[0];
f11[2] = i ? in2[iy2][ix2] : fill[1];
f11[3] = i ? in3[iy2][ix2] : fill[2];
dx = x - ix1;
dy = y - iy1;
dxy = dx * dy;
for (i = 1; i <= 3; i++)
{
pdx[i] = f01[i] - f00[i];
pdy[i] = f10[i] - f00[i];
pdxy[i] = f11[i] - f00[i];
ff[i] = f00[i] + dx * pdx[i] + dy * pdy[i] +
0.5 * dxy * (pdxy[i] - pdx[i] - pdy[i]) + 0.7;
if (ff[i] >= PCMAX)
ff[i] = PCMAX - 1;
if (ff[i] < 0.0)
ff[i] = 0.0;
}
*po1 = ff[1];
*po2 = ff[2];
*po3 = ff[3];
}
/*****************************************************************
* Apply the transfomration matrix M and shift vector M to
* R,G,B simultaneously with subpixel sampling taking care of
****************************************************************/
#if 1
static int
tran2d_rgb(pc_t **in[], pc_t **out[],
float m[][2], float shift[], int fill[3])
{
register float *prex, *prey, tmpy, tmpx;
register pc_t *ras1 = out[0][0], *ras2 = out[1][0], *ras3 = out[2][0];
register pc_t **in1 = in[0], **in2 = in[1], **in3 = in[2];
register int c, ic, ir;
int r;
long rlines;
PRE_tran(prex, prey, "Tran2dRGB");
rlines = progress_report("Tran2D_RGB ...", nrow);
for (r = 0; r < nrow; r++)
{
REPORT(r, rlines);
if (!subpix)
{
tmpx = m[0][1] * (r - shift[1]) + 0.5;
tmpy = m[1][1] * (r - shift[1]) + 0.5;
for (c = 0; c < ncol; c++, ras1++, ras2++, ras3++)
{
if (OutsideSRC(c, ir, ic))
{
*ras1 = fill[0];
*ras2 = fill[1];
*ras3 = fill[2];
}
else
{
*ras1 = in1[ir][ic];
*ras2 = in2[ir][ic];
*ras3 = in3[ir][ic];
}
}
}
else
{
register float dr, dc;
tmpx = m[0][1] * (r - shift[1]) + 0.2;
tmpy = m[1][1] * (r - shift[1]) + 0.2;
for (c = 0; c < ncol; c++, ras1++, ras2++, ras3++)
{
dr = (tmpy + prey[c]);
dc = (tmpx + prex[c]);
if (dr < -1.0 || dr > row || dc < -1.0 || dc > col)
{
*ras1 = fill[0];
*ras2 = fill[1];
*ras3 = fill[2];
}
else
{
frac(ras1, in1, ras2, in2, ras3, in3, dc, dr, fill);
}
}
}
}
POST_tran(prex, prey);
remove_progress_report();
return 0;
}
#else
static void
weight(pc_t *po1, pc_t **in1, pc_t *po2, pc_t **in2,
pc_t *po3, pc_t **in3, float x[], float y[])
{
register int ix, iy;
register float h, w, sum1, sum2, sum3, area;
sum1 = sum2 = sum3 = area = 0.0;
for (iy = y[0]; iy <= y[1]; iy++)
{
if (iy < y[0])
h = (float) iy + 1.0 - y[0];
else if ((float) iy + 1.0 > y[1])
h = y[1] - iy;
else
h = 1.0;
for (ix = x[0]; ix <= x[1]; ix++)
{
if (ix < x[0])
w = (float) ix + 1.0 - x[0];
else if ((float) ix + 1.0 > x[1])
w = x[1] - ix;
else
w = 1.0;
sum1 += (float) in1[iy][ix] * (h * w);
sum2 += (float) in2[iy][ix] * (h * w);
sum3 += (float) in3[iy][ix] * (h * w);
area += (h * w);
}
}
sum1 /= area;
sum2 /= area;
sum3 /= area;
if (sum1 >= PCMAX)
sum1 = PCMAX - 1;
if (sum2 >= PCMAX)
sum2 = PCMAX - 1;
if (sum3 >= PCMAX)
sum3 = PCMAX - 1;
if (sum1 < 0.0)
sum1 = 0.0;
if (sum2 < 0.0)
sum2 = 0.0;
if (sum3 < 0.0)
sum3 = 0.0;
*po1 = (0.5 + sum1);
*po2 = (0.5 + sum2);
*po3 = (0.5 + sum3);
}
/*
* the transformation routine for seperate RGB's
*/
static int
tran2d_rgb(pc_t **in[], pc_t **out[],
const float m[][2], const float shift[], int fill[3])
{
register float *prex, *prey, tmpy, tmpx;
register pc_t *ras1 = out[0][0], *ras2 = out[1][0], *ras3 = out[2][0];
register pc_t **in1 = in[0], **in2 = in[1];
**in3 = in[2];
register int c, ic, ir;
int r;
long rlines;
PRE_tran(prex, prey, "Tran2dRGB");
rlines = progress_report("Tran2D_RGB ...", nrow);
for (r = 0; r < nrow; r++)
{
REPORT(r, rlines);
if (!subpix)
{
tmpx = m[0][1] * (r - shift[1]) + 0.5;
tmpy = m[1][1] * (r - shift[1]) + 0.5;
for (c = 0; c < ncol; c++, ras1++, ras2++, ras3++)
{
if (OutsideSRC(c, ir, ic))
{
*ras1 = fill[0];
*ras2 = fill[1];
*ras3 = fill[2];
}
else
{
*ras1 = in1[ir][ic];
*ras2 = in2[ir][ic];
*ras3 = in3[ir][ic];
}
}
}
else
{
register float dr, dc;
tmpx = m[0][1] * (r - shift[1]);
tmpy = m[1][1] * (r - shift[1]);
for (c = 0; c < ncol; c++, ras1++, ras2++, ras3++)
{
dr = (tmpy + prey[c]);
dc = (tmpx + prex[c]);
if (dr < -1.0 || dr > row || dc < -1.0 || dc > col)
{
*ras1 = fill[0];
*ras2 = fill[1];
*ras3 = fill[2];
}
else
{
weight(ras1, in1, ras2, in2, ras3, in3, dc, dr, fill);
}
}
}
}
POST_tran(prex, prey);
remove_progress_report();
return 0;
}
#endif
/*****************************************************************
* Transform a primary color matrix
****************************************************************/
static int
tran2d_pc(pc_t **in1, pc_t **out1,
float m[][2], float shift[], int fill)
{
register float *prex, *prey, tmpy, tmpx;
register pc_t *ras1 = out1[0];
register int c, ic, ir;
int r;
long rlines;
PRE_tran(prex, prey, "Tran2dRGB");
rlines = progress_report("Tran2D_PC ...", nrow);
for (r = 0; r < nrow; r++)
{
REPORT(r, rlines);
if (!subpix)
{
tmpx = m[0][1] * (r - shift[1]) + 0.5;
tmpy = m[1][1] * (r - shift[1]) + 0.5;
for (c = 0; c < ncol; c++, ras1++)
*ras1 = OutsideSRC(c, ir, ic) ? fill : in1[ir][ic];
}
else
{
register float dr, dc, drc;
register int ir1, ic1;
tmpx = m[0][1] * (r - shift[1]);
tmpy = m[1][1] * (r - shift[1]);
for (c = 0; c < ncol; c++, ras1++)
{
ir = (0.2 + (dr = (tmpy + prey[c])));
ic = (0.2 + (dc = (tmpx + prex[c])));
dr = dr - ir;
dc = dc - ic;
if (dr < 0.0)
{
dr = dr + 1.0;
ir--;
}
if (dc < 0.0)
{
dc = dc + 1.0;
ic--;
}
if (ir < 0 || ir >= row || ic < 0 || ic >= col)
{
*ras1 = fill;
}
else
{
drc = dr * dc;
ir1 = ir + (ir < row - 1);
ic1 = ic + (ic < col - 1);
GETFRAC(*ras1, ir, ic, ir1, ic1, dr, dc, drc, in1, 0, PCMAX - 1);
}
}
}
}
POST_tran(prex, prey);
remove_progress_report();
return 0;
}
/*****************************************************************
* global interface for 2D raster transformations. Note that fill color
* could be either in RGBA format or just an index depending on the
* image type
******************************************************************/
int
img_tran(IPTR im, int nh, int nw, float m[][2], float shift[],
rgba_t fill, int subp)
{
pc_t **pcm[3];
void *rm;
int status, fc[3];
set_tran_size(im->h, im->w, nh, nw);
set_tran_subpix(subp);
if (!subp)
{ /* simple transformation */
rm = get_mat(nh, nw, im->esize);
status = IS_CI(im) ? tran2d_ci(im->mraster, rm, m, shift, fill) :
tran2d_cpack(im->mraster, rm, m, shift, fill);
if (status >= 0)
fill_image_struct(im, rm, nh, nw, im->type);
return status;
}
/* with subpixel sampling */
if (img_get_rgb(im) < 0)
return -1;
#ifdef TIGHT_MEM
if (im->size > MAXMEMK)
img_free_rastermem(im);
#endif
if (IS_CI(im))
{
get_cmap_entry(im->cmap, fc, fill);
fill = RGB2CPACK(fc[0], fc[1], fc[2]);
}
else
{
CPACK2RGB(fill, fc[0], fc[1], fc[2]);
}
if (IS_GRAY(im))
{
if (!(pcm[0] = pcm[1] = pcm[2] = get_mat(nh, nw, sizeof(pc_t))))
return -1;
}
else
{
if (!(pcm[0] = get_mat(nh, nw, sizeof(pc_t))) ||
! (pcm[1] = get_mat(nh, nw, sizeof(pc_t))) ||
! (pcm[2] = get_mat(nh, nw, sizeof(pc_t))))
return -1;
}
status = IS_GRAY(im) ?
tran2d_pc(im->pc[0], pcm[0], m, shift, fc[0]) :
tran2d_rgb(im->pc, pcm, m, shift, fc);
if (status >= 0)
{
im->w = nw;
im->h = nh;
img_replace_rgb(im, pcm[0], pcm[1], pcm[2]);
img_rgb_to_cpack(im);
img_free_rgbmem(im);
}
return status;
}
/****************************************************************
* Scaling an image.
************************************************************{***/
/*********************************************************
* Scale without subpixel sampling
********************************************************/
static int
scale_simple(IPTR im, int nh, int nw)
{
void *nm, *om = im->mraster;
double xfac = (double) im->w / nw, yfac = (double) im->h / nh;
int *lut, j, jj;
long rlines;
/* generate a lookup table */
if (!(lut = malloc(sizeof(int) * (nw + 1))))
return -1;
if (!(nm = get_mat(nh, nw, im->esize)))
return -1;
rlines = progress_report("Scale(Simple) ...", nh);
for (j = 0; j < nw; j++)
lut[j] = (xfac * j + 0.001);
if (IS_CI(im))
{
register ci_t *nrows, *orows;
register int i;
for (j = 0; j < nh; j++)
{
jj = (yfac * j + 0.001);
REPORT(j, rlines);
nrows = ((ci_t **) nm)[j];
orows = ((ci_t **) om)[jj];
for (i = 0; i < nw; i++)
{
*(nrows + i) = *(orows + lut[i]);
}
}
}
else
{
register rgba_t *nrows, *orows;
register int i;
for (j = 0; j < nh; j++)
{
REPORT(j, rlines);
jj = (yfac * j + 0.001);
nrows = ((rgba_t **) nm)[j];
orows = ((rgba_t **) om)[jj];
for (i = 0; i < nw; i++)
{
*(nrows + i) = *(orows + lut[i]);
}
}
}
free(lut);
remove_progress_report();
return fill_image_struct(im, nm, nh, nw, im->type);
}
/*********************************************************************
* Blend the adjucent pixels, good for scaling factors between 0.5 and 2.0.
* Grayscale images are handled same as color images memoerywise,
* but faster.
*********************************************************************/
static int
scale_blend(pc_t **om[], pc_t **nm[],
int h, int w, int nh, int nw, int comp)
{
double xs = (double) nw / w, ys = (double) nh / h, off;
int *lut, *buf[3];
int i, j, ii, jj, ii1;
long rlines;
register float fi, *flut;
register pc_t *r1, *g1, *b1;
register pc_t *r2, *g2, *b2;
rlines = progress_report("Scaling(Fast) ...", nh);
lut = malloc(sizeof(int) * (nw + 1));
flut = malloc(sizeof(float) * (nw + 1));
/* offset is critical for high frequency data */
off = (1.0 - xs);
for (i = 0; i < nw; i++)
{
flut[i] = (double) i / xs + off;
if (flut[i] < 0.0)
flut[i] = 0.0;
lut[i] = flut[i];
flut[i] = flut[i] - lut[i];
}
/* now do it */
/* get the buffers */
for (i = 0; i < 3; i++)
buf[i] = malloc(sizeof(int) * (w + 2));
off = (1.0 - ys);
for (j = 0; j < nh; j++)
{
fi = (double) j / ys + off;
if (fi < 0.0)
fi = 0.0;
jj = fi;
fi = fi - jj;
REPORT(j, rlines);
r1 = om[0][jj];
r2 = om[0][jj + (jj < (h - 1))];
if (comp == 1)
{ /* gray images */
for (i = 0; i < w; i++, r1++, r2++)
{
buf[0][i] = buf[1][i] = buf[2][i] =
(Lintp((int) *r1, (int) *r2, fi) + 0.5);
}
}
else
{ /* color image */
g1 = om[1][jj];
b1 = om[2][jj];
g2 = om[1][jj + (jj < (h - 1))];
b2 = om[2][jj + (jj < (h - 1))];
/* make a new row */
for (i = 0; i < w; i++, r1++, g1++, b1++, r2++, g2++, b2++)
{
buf[0][i] = (Lintp((int) *r1, (int) *r2, fi) + 0.5);
buf[1][i] = (Lintp((int) *g1, (int) *g2, fi) + 0.5);
buf[2][i] = (Lintp((int) *b1, (int) *b2, fi) + 0.5);
}
}
buf[0][w] = buf[0][w - 1];
buf[1][w] = buf[1][w - 1];
buf[2][w] = buf[2][w - 1];
r1 = nm[0][j];
g1 = nm[1][j];
b1 = nm[2][j];
/* scale x */
if (comp == 1)
{
for (i = 0; i < nw; i++, r1++, g1++, b1++)
{
ii = lut[i];
fi = flut[i];
ii1 = ii + 1;
*r1 = *g1 = *b1 =
(Lintp(buf[0][ii], buf[0][ii1], fi) + 0.5);
}
}
else
{
for (i = 0; i < nw; i++, r1++, g1++, b1++)
{
ii = lut[i];
fi = flut[i];
ii1 = ii + 1;
*r1 = (Lintp(buf[0][ii], buf[0][ii1], fi) + 0.5);
*g1 = (Lintp(buf[1][ii], buf[1][ii1], fi) + 0.5);
*b1 = (Lintp(buf[2][ii], buf[2][ii1], fi) + 0.5);
}
}
}
free(lut);
free(flut);
free(buf[0]);
free(buf[1]);
free(buf[2]);
remove_progress_report();
return 0;
}
/**********************************************************************
* Scaling with proper averaging: Slow, but results are good. For some
* reason, better than PNMSCALE with high frequency data
***********************************************************************/
static int
scale_proper(pc_t **om[], pc_t **nm[],
int h, int w, int nh, int nw, int comp)
{
float xt = (double) w / nw;
float yt = (double) h / nh;
float s1, s2, s3, area, delta;
float width, height;
float x1, x2, y1, y2;
int x, y, i, j;
long rlines;
rlines = progress_report("Scale(HiFi) ...", nh);
for (y1 = 0.0, j = 0; j < nh; j++, y1 += yt)
{
y2 = y1 + yt;
if (y2 > h)
y2 = h;
REPORT(j, rlines);
for (x1 = 0.0, i = 0; i < nw; i++, x1 += xt)
{
s1 = s2 = s3 = area = 0.0;
x2 = x1 + xt;
if (x2 > w)
x2 = w;
for (y = (int) y1; y < y2; y++)
{
if (y < y1)
height = y + 1.0 - y1;
else if ((y + 1.0) > y2)
height = y2 - y;
else
height = 1.0;
for (x = (int) x1; x < x2; x++)
{
if (x < x1)
width = x + 1.0 - x1;
else if ((x + 1.0) > x2)
width = x2 - x;
else
width = 1.0;
area += (delta = width * height);
s1 += delta * om[0][y][x];
if (comp == 1)
{
s2 = s3 = s1;
}
else
{
s2 += delta * om[1][y][x];
s3 += delta * om[2][y][x];
}
}
}
nm[0][j][i] = (s1 / area);
nm[1][j][i] = (s2 / area);
nm[2][j][i] = (s3 / area);
}
}
remove_progress_report();
return 0;
}
/*************************************************************
* SCALING dispatcher. All types.
* If magnification with integer factor, no subpixel applies.
* If shrinking with factor greater than 0.5, use simple blending
* else do box average.
************************************************************/
int
img_scale(IPTR im, int nh, int nw, int subp, int fit, rgba_t fill)
{
float m[2][2], shift[2];
double xs, ys, ts;
int ss[2], err, i;
pc_t **nm[3];
xs = (double) im->w / nw;
ys = (double) im->h / nh;
/* if fitting is requested with equal scaling, disable it */
if (fit && (Abs(xs - ys) < 0.00001))
{
fit = 0;
M_info("Scale", "Disabling fit");
}
/* fit feature is uniq, can't be handled by scaling alone */
if (fit)
{
ts = (xs < ys) ? ys : xs;
ss[1] = (((double) nh - im->h / ts + 0.2)) / 2;
ss[0] = (((double) nw - im->w / ts + 0.2)) / 2;
xs = ys = ts;
shift[0] = ss[0];
shift[1] = ss[1];
/* get the transformation matrix */
m[0][0] = xs;
m[1][1] = ys;
m[0][1] = m[1][0] = 0.0;
err = img_tran(im, nh, nw, m, shift, fill, subp) < 0;
}
/*
* if no subpixel sampling requested, or magnification with integer
* factors, use pixel replication
*/
else if (!subp || ((nw % im->w) == 0 && (nh % im->h) == 0))
{
err = scale_simple(im, nh, nw);
}
else
{ /* anti-aliasing */
if (img_get_rgb(im) < 0)
return -1;
#ifdef TIGHT_MEM
if (im->size > MAXMEMK)
img_free_rastermem(im);
#endif
for (i = err = 0; !err && i < 3; i++)
err = (!(nm[i] = get_mat(nh, nw, sizeof(pc_t))));
err = err || ((subp < 2) ? scale_blend : scale_proper)
(im->pc, nm, im->h, im->w, nh, nw, IS_GRAY(im));
if (err)
return -1;
im->w = nw;
im->h = nh;
img_replace_rgb(im, nm[0], nm[1], nm[2]);
img_rgb_to_cpack(im);
img_free_rgbmem(im);
}
return err;
}
/******************************************************************
* GUI part of scaling
*****************************************************************/
static double xscale, yscale; /* current scale */
static double xsize, ysize; /* current size */
static int fillcol[4]; /* fill color */
static int showsize; /* reporint control */
static int subp = 1, exact, keepasp = 1;
static int ch, cw, minw;
static void create_form_fscale(void);
#define MAXSC 10.0 /* maximum scale factor */
#define MINSC 0.01 /* minimum scale factor */
static FL_FORM *fscale;
static FL_OBJECT *xcnt, *ycnt, *scaleit, *scaledone;
static FL_OBJECT *ssubp[3];
static void
new_size(IPTR im)
{
int sh, sw;
ysize = ch = im->h;
xsize = cw = im->w;
xscale = yscale = 1.0;
sh = (MINSC * ch);
sw = (MINSC * cw);
/* round it down to nice numbers */
minw = (sh > sw) ? sw : sh;
SET_NICE(minw);
}
/***** Set current scaling factors or scaled size ******/
static void
set_counter(void)
{
fl_freeze_form(fscale);
if (showsize)
{
fl_set_counter_value(xcnt, xsize);
fl_set_counter_value(ycnt, ysize);
}
else
{
fl_set_counter_value(xcnt, xscale);
fl_set_counter_value(ycnt, yscale);
}
fl_unfreeze_form(fscale);
}
/********* Show current scaling factors or scaled size *********/
/* ARGSUSED */
static void
show_size(FL_OBJECT * ob, long p)
{
fl_freeze_form(fscale);
if ((showsize = p))
{
fl_set_counter_precision(xcnt, 0);
fl_set_counter_bounds(xcnt, minw, MAXSC * cw);
fl_set_counter_precision(ycnt, 0);
fl_set_counter_bounds(ycnt, minw, MAXSC * ch);
fl_set_counter_step(xcnt, minw, 5 * minw);
fl_set_counter_step(ycnt, minw, 5 * minw);
}
else
{
fl_set_counter_precision(xcnt, 2);
fl_set_counter_precision(ycnt, 2);
fl_set_counter_bounds(xcnt, MINSC, MAXSC);
fl_set_counter_bounds(ycnt, MINSC, MAXSC);
fl_set_counter_step(xcnt, MINSC, 10 * MINSC);
fl_set_counter_step(ycnt, MINSC, 10 * MINSC);
}
set_counter();
fl_unfreeze_form(fscale);
}
/*****************************************************************
* this one is called from the control panel. Return negative value
* to indicate failure and suppress redraw in driver
*******************************************************************/
int
do_scale(IPTR im)
{
FL_OBJECT *ret;
short val;
int status = 0, ncols, nrows;
int changed = 0;
create_form_fscale();
/* For BW images, set default to no subpixel */
if (IS_BW(im))
{
subp = 0;
fl_set_button(ssubp[0], subp == 0);
fl_set_button(ssubp[1], subp == 1);
fl_set_button(ssubp[2], subp == 2);
}
new_size(im);
show_size(0, showsize);
bit_show_form(fscale, FL_PLACE_MOUSE, 0, "Scale");
while (status >= 0 && (ret = fl_do_forms()) != scaledone)
{
if (ret == FL_EVENT)
(void) bit_qread(&val);
else if (ret == scaleit)
{
ncols = (0.6 + xsize);
nrows = (0.6 + ysize);
if (nrows == im->h && ncols == im->w)
continue;
changed = 1;
show_busy("");
status = img_scale(im, nrows, ncols, subp, exact,
IS_CI(im) ? fillcol[3] :
Pack(fillcol[0], fillcol[1], fillcol[2]));
end_busy();
if (status >= 0)
im->io->display(im, -1, 0);
new_size(im);
show_size(0, showsize);
}
}
bit_hide_form(fscale);
return (status >= 0 && changed) ? 0 : -1;
}
/*
* call back routine if there is changes in counter. note that set_counter
* must be called if aspect is to be kept
*/
/* ARGSUSED */
static void
counter_cb(FL_OBJECT * p, long q)
{
double x = fl_get_counter_value(xcnt);
double y = fl_get_counter_value(ycnt);
if (showsize)
{
xscale = (xsize = x) / cw;
yscale = (ysize = y) / ch;
}
else
{
xsize = ((xscale = x) * cw);
ysize = ((yscale = y) * ch);
}
if (keepasp && !exact)
{
if (q == 1)
{ /* x counter */
yscale = xscale;
}
else
{
xscale = yscale;
}
xsize = xscale * cw;
ysize = xscale * ch;
}
set_counter();
}
/******** Show fill color *******/
#define FILLCI 257 /* fill color form index */
/* ARGSUSED */
extern IPTR imgptr;
static void
fill_cb(FL_OBJECT * ob, long q)
{
/* block until satisfactory */
get_color(imgptr, fillcol, 1);
fl_mapcolor(FILLCI, fillcol[0], fillcol[1], fillcol[2]);
fl_redraw_object(ob);
}
/* this two must not be 0, 1, 2 */
#define KEEPASP 10
#define EXACT 11
/* set options */
static void
misc_cb(FL_OBJECT * ob, long p)
{
switch (p)
{
case KEEPASP:
keepasp = fl_get_button(ob);
break;
case EXACT:
if ((exact = fl_get_button(ob)))
{ /* set change to 1pix */
if (showsize)
{
fl_set_counter_step(xcnt, 1.0, 5 * minw);
fl_set_counter_step(ycnt, 1.0, 5 * minw);
}
}
break;
default:
subp = p;
break;
}
}
static void
create_form_fscale(void)
{
FL_OBJECT *obj;
static int ok;
if (ok)
return;
fscale = fl_bgn_form(FL_UP_BOX, 250.0, 240.0);
obj = fl_add_button(FL_HIDDEN_BUTTON, 0, 0, 250, 240, "");
fl_set_call_back(obj, help_cb, HELP_SCALE);
obj = fl_add_text(FL_NT, 30.0, 200.0, 205.0, 25.0, "RasterScale");
fl_set_object_lcol(obj, 4);
fl_set_object_lsize(obj, 16.000000);
fl_set_object_align(obj, FL_ALIGN_CENTER);
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
/* the x,y counters */
xcnt = fl_add_counter(FL_NC, 50.0, 115.0, 160.0, 25.0, "X");
fl_set_object_lcol(xcnt, 4);
fl_set_object_lsize(xcnt, 10.0);
fl_set_object_align(xcnt, FL_ALIGN_LEFT);
fl_set_object_lstyle(xcnt, FL_BOLD_STYLE);
fl_set_call_back(xcnt, counter_cb, 1);
ycnt = fl_add_counter(FL_NC, 50.0, 90.0, 160.0, 25.0, "Y");
fl_set_object_lcol(ycnt, 4);
fl_set_object_lsize(ycnt, 10.0);
fl_set_object_align(ycnt, FL_ALIGN_LEFT);
fl_set_object_lstyle(ycnt, FL_BOLD_STYLE);
fl_set_call_back(ycnt, counter_cb, 0);
/* what to show */
fl_bgn_group();
obj = fl_add_roundbutton(FL_RB, 15.0, 50.0, 30.0, 30.0, "ShowSize");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, show_size, 1);
obj = fl_add_roundbutton(FL_RB, 130.0, 50.0, 30.0, 30.0, "ShowScale");
fl_set_object_lsize(obj, 10.0);
fl_set_button(obj, 1);
fl_set_call_back(obj, show_size, 0);
fl_end_group();
/* misc settings */
fl_bgn_group();
ssubp[0] = fl_add_roundbutton(FL_RB, 10.0, 170.0, 30.0, 30.0, "NoAA");
fl_set_object_lsize(ssubp[0], 10.000);
fl_set_button(ssubp[0], (subp == 0));
fl_set_call_back(ssubp[0], misc_cb, 0);
ssubp[1] = fl_add_roundbutton(FL_RB, 90.0, 170.0, 30.0, 30.0, "FastAA");
fl_set_object_lsize(ssubp[1], 10.000);
fl_set_button(ssubp[1], (subp == 1));
fl_set_call_back(ssubp[1], misc_cb, 1);
ssubp[2] = fl_add_roundbutton(FL_RB, 170.0, 170.0, 30.0, 30.0, "Hi-Fi");
fl_set_object_lsize(ssubp[2], 10.000);
fl_set_button(ssubp[2], (subp == 2));
fl_set_call_back(ssubp[2], misc_cb, 2);
fl_end_group();
obj = fl_add_roundbutton(FL_PB, 10.0, 145.0, 30.0, 30.0, "KeepASP");
fl_set_object_lsize(obj, 10.0);
fl_set_button(obj, keepasp);
fl_set_call_back(obj, misc_cb, KEEPASP);
obj = fl_add_roundbutton(FL_PB, 90.0, 145.0, 30.0, 30.0, "Exact");
fl_set_object_lsize(obj, 10.0);
fl_set_button(obj, exact);
fl_set_call_back(obj, misc_cb, EXACT);
/* control buttons */
scaledone = obj = fl_add_button(FL_NB, 100.0, 10.0, 65.0, 25.0, "Done");
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
scaleit = obj = fl_add_button(FL_NB, 165.0, 10.0, 65.0, 25.0, "OK");
fl_set_object_color(obj, 47, 2);
fl_set_object_lsize(obj, 10.0);
obj = fl_add_button(FL_NB, 40.0, 10.0, 60.0, 25.0, "FillColor");
fl_set_object_color(obj, FILLCI, FL_BLUE);
fl_set_object_lcol(obj, FL_MAGENTA);
fl_set_object_lsize(obj, 10.0);
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
fl_mapcolor(FILLCI, fillcol[0], fillcol[1], fillcol[2]);
fl_set_call_back(obj, fill_cb, 0);
fl_end_form();
ok = 1;
}
/******************************************************************
* END of Scaling
***************************************************************}*/
/***************************************************************
* Rotation
*************************************************************/
#include <math.h>
static int iangle = 90; /* rotation angle */
static int aaopt = 1; /* weather anti-aliasing */
static int lc[4]; /* fill color */
/*********************************************************************
* rotate an image by 90 or -90 or multiples of it. Rotate 180
* could've been done in place by rotatematrix.
*******************************************************************/
static int
image_rot_special(IPTR im, int deg)
{
void *p;
int oh = im->h, ow = im->w;
show_busy("Rotating ...");
p = rotate_mat(im->mraster, oh, ow, deg, im->esize);
end_busy();
if (!p)
return -1;
return (deg == 90 || deg == -90) ?
fill_image_struct(im, p, ow, oh, im->type) :
fill_image_struct(im, p, oh, ow, im->type);
}
/*************************************************
* Mirrors. dir= 0: X-mirror, dir=1 Y-mirror
*************************************************/
static int
image_mirror(IPTR im, int dir)
{
int p;;
if ((p = flip_mat(im->mraster, im->h, im->w, dir, im->esize) >= 0))
im->io->display(im, -1, 0);
return p ? 0 : -1;
}
/**************************************************************
* Rotation routine dispatcher
*************************************************************/
static int
ras_rotate(IPTR im, int angle, int aa, rgba_t fill)
{
float pi = acos(-1.0), fa, absin;
double sina, cosa, xoff, yoff;
float tmat[2][2], shift[2];
int nw, nh, h, w, status = 0;
/* normalize to within (-180,180) */
while (angle < -180)
angle += 360;
while (angle > 180)
angle -= 360;
/* check for special angles first */
iangle = angle;
/* normalize the angle between (-90,90). Flip if neccessary */
if (angle == 180 || angle == -180)
{
angle = 0;
status = image_rot_special(im, 180);
}
else if (angle >= 90)
{
angle -= 90;
status = image_rot_special(im, 90);
}
else if (angle <= -90)
{
angle += 90;
status = image_rot_special(im, -90);
}
if (status < 0)
{
Bark("RotateSpecial", "Something is wrong");
return -1;
}
if (angle == 0)
{
im->io->display(im, -1, 0);
return 0;
}
/* following code assumed that the angle is between (-90,90) */
w = im->w;
h = im->h;
iangle = angle;
fa = angle * pi / 180.0;
cosa = cos(fa);
sina = sin(fa);
absin = sina > 0.0 ? sina : -sina;
nw = (cosa * w + absin * h + 1.9999);
nh = (cosa * h + absin * w + 1.9999);
update_size_info(nw, nh);
if (angle > 0)
{
xoff = absin * h;
yoff = 0;
}
else
{
xoff = 0;
yoff = absin * w;
}
/**********************************************
* / \
* | cos(theta) sin(theta) |
* |-sin(theta) cos(theta) |
* \ /
***********************************************/
tmat[0][0] = tmat[1][1] = cosa;
tmat[0][1] = sina;
tmat[1][0] = -tmat[0][1];
shift[0] = (xoff + 0.99);
shift[1] = (yoff + 0.99);
if ((status = img_tran(im, nh, nw, tmat, shift, fill, aa)) >= 0)
im->io->display(im, -1, 0);
return status;
}
/********************************************************
* GUI part of rotation
*******************************************************/
static FL_FORM *rrot;
static FL_OBJECT *done, *doit, *rptext;
#define XMIRROR 900
#define YMIRROR 1800
static void create_form_rrot(void);
/**************************************************************
* Global entry point for rotation
*************************************************************/
int
do_rotate(IPTR im)
{
FL_OBJECT *ret;
short val;
int changed = 0;
create_form_rrot();
deactivate_all_forms();
bit_show_form(rrot, FL_PLACE_MOUSE, 0, "Rotate");
while ((ret = fl_do_forms()) != done)
{
if (ret == doit && iangle != 0)
{
changed = 1;
if (iangle == XMIRROR)
{
image_mirror(im, 'x');
}
else if (iangle == YMIRROR)
{
image_mirror(im, 'y');
}
else
{
ras_rotate(im, iangle, aaopt, IS_CI(im) ?
lc[3] : Pack(lc[0], lc[1], lc[2]));
}
}
else if (ret == FL_EVENT)
(void) bit_qread(&val);
}
fl_activate_all_forms();
bit_hide_form(rrot);
return changed ? 0 : -1;
}
/* ARGSUSED */
static void
set_opt(FL_OBJECT * a, long p)
{
aaopt = p;
}
#define LSTEP_ 10
#define SSTEP_ 1
/* ARGSUSED */
static void
set_angle(FL_OBJECT * a, long p)
{
char sreport[50];
static int langle;
if (iangle > 360)
iangle = langle;
switch (p)
{
case 66:
iangle += LSTEP_;
break;
case 6:
iangle += SSTEP_;
break;
case 44:
iangle -= LSTEP_;
break;
case 4:
iangle -= SSTEP_;
break;
}
if (iangle > 180)
iangle -= 360;
else if (iangle < -180)
iangle += 360;
sprintf(sreport, "%d", iangle);
langle = iangle;
fl_set_object_label(rptext, sreport);
}
/*********** Show fill color ***********************/
#define RFILLCI 258 /* fill color form index */
/* ARGSUSED */
static void
set_fill(FL_OBJECT * ob, long p)
{
get_color(imgptr, lc, 1); /* block */
fl_mapcolor(RFILLCI, lc[0], lc[1], lc[2]);
fl_redraw_object(ob);
}
/* ARGSUSED */
static void
set_mirror(FL_OBJECT * a, long p)
{
const char *rp = 0;
iangle = p;
if (p == XMIRROR)
{
rp = "Flip about X";
}
else if (p == YMIRROR)
{
rp = "Flip about Y";
}
else if (p == 90)
{
rp = "90";
}
else if (p == -90)
{
rp = "-90";
}
else if (p == 180)
{
rp = "180";
}
fl_set_object_label(rptext, rp);
}
static void
create_form_rrot(void)
{
FL_OBJECT *obj;
static int ok;
if (ok)
return;
rrot = fl_bgn_form(FL_NO_BOX, 245.0, 250.0);
obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 245.0, 250.0, "");
fl_set_object_color(obj, 9, 47);
obj = fl_add_button(FL_HB, 0.0, 0.0, 245, 245, "");
fl_set_call_back(obj, help_cb, HELP_ROTATE);
obj = fl_add_text(FL_NT, 30.0, 210.0, 195.0, 25.0, "RasterRotation");
fl_set_object_lcol(obj, 4);
fl_set_object_lsize(obj, 12.00);
fl_set_object_align(obj, FL_ALIGN_CENTER);
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
/* control */
done = obj = fl_add_button(FL_NB, 85.0, 10.0, 70.0, 25.0, "Done");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
doit = obj = fl_add_button(FL_NB, 155.0, 10.0, 75.0, 25.0, "OK");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 2);
fl_set_object_lsize(obj, 10.0);
/* option group */
fl_bgn_group();
obj = fl_add_roundbutton(FL_RB, 10.0, 45.0, 30.0, 30.0, "no anti-aliasing");
fl_set_object_color(obj, 7, 1);
fl_set_object_lsize(obj, FL_SMALL_FONT);
fl_set_call_back(obj, set_opt, 0);
obj = fl_add_roundbutton(FL_RB, 105.0, 45.0, 30.0, 30.0, "Anit-aliasing");
fl_set_object_color(obj, 7, 1);
fl_set_object_lsize(obj, FL_SMALL_FONT);
fl_set_button(obj, 1);
fl_set_call_back(obj, set_opt, 1);
obj = fl_add_roundbutton(FL_RB, 190.0, 45.0, 30.0, 30.0, "hi-fi");
fl_set_object_color(obj, 7, 1);
fl_set_object_lsize(obj, FL_SMALL_FONT);
fl_set_call_back(obj, set_opt, 2);
fl_end_group();
/* mirror and misc groups */
obj = fl_add_button(FL_NB, 20.0, 170.0, 70.0, 25.0, "X-mirror");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, set_mirror, XMIRROR);
obj = fl_add_button(FL_NB, 90.0, 170.0, 70.0, 25.0, "Y-mirror");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, set_mirror, YMIRROR);
obj = fl_add_button(FL_NB, 160.0, 170.0, 70.0, 25.0, "180");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, set_mirror, 180);
obj = fl_add_button(FL_NB, 20.0, 145.0, 70.0, 25.0, "+90");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, set_mirror, 90);
obj = fl_add_button(FL_NB, 90.0, 145.0, 70.0, 25.0, "-90");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, set_mirror, -90);
/* fill color stuff */
obj = fl_add_button(FL_NB, 160.0, 145.0, 70.0, 25.0, "FillColor");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, RFILLCI, 3);
fl_mapcolor(RFILLCI, lc[0], lc[1], lc[2]);
fl_set_object_lsize(obj, 10.0);
fl_set_object_lcol(obj, FL_MAGENTA);
fl_set_call_back(obj, set_fill, 0);
/* reporting */
obj = fl_add_text(FL_NT, 60.0, 110.0, 130.0, 15.0, "Current Rotation");
fl_set_object_lcol(obj, 4);
fl_set_object_lsize(obj, 10.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
rptext = obj = fl_add_text(FL_NT, 85.0, 80.0, 75.0, 25.0, "90");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lcol(obj, 4);
fl_set_object_lsize(obj, 10.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
/* Angle setting group */
obj = fl_add_button(FL_TOUCH_BUTTON, 185.0, 80.0, 25.0, 25.0, ">>");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 9);
fl_set_call_back(obj, set_angle, 66);
obj = fl_add_button(FL_TOUCH_BUTTON, 160.0, 80.0, 25.0, 25.0, ">");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 9);
fl_set_call_back(obj, set_angle, 6);
obj = fl_add_button(FL_TOUCH_BUTTON, 60.0, 80.0, 25.0, 25.0, "<");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 9);
fl_set_call_back(obj, set_angle, 4);
obj = fl_add_button(FL_TOUCH_BUTTON, 35.0, 80.0, 25.0, 25.0, "<<");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, 47, 9);
fl_set_call_back(obj, set_angle, 44);
fl_end_form();
ok = 1;
}